package depta.dataregion;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.htmlparser.Node;
import org.htmlparser.util.NodeList;
import org.lobobrowser.html.domimpl.NodeListImpl;


import util.nodecloner.ChildFinder;
import util.nodecloner.NodeCloner;

import depta.treematching.EnhancedSimpleTreeMatching;

public class MDR2 {
	
	private static final int FIRST_LEVEL = 3;
	private Map<Node, Map<Integer, Map<Node, Map<Integer, Map<Node, Double>>>>> info;

	public void MDR2Algorithm(Node n, int K){
		if (isAbove(n,FIRST_LEVEL)){
			
			combComp(ChildFinder.getChildren(n),n,K);
		
			NodeList nl = ChildFinder.getChildren(n);
			
			for (int i = 0; i < nl.size(); i++) {
				
				MDR2Algorithm(nl.elementAt(i), K);
			}
		}
	}

	public static boolean isAbove(Node n, int firstLevel) {
		if (firstLevel == 0)
			return true;
		
		NodeList nList = ChildFinder.getChildren(n);
		
		for (int i = 0; i < nList.size(); i++) {
			
			if (isAbove(nList.elementAt(i),firstLevel-1))
				return true;
			
		}
		
		return false;
	}

	private void combComp(NodeList nodes, Node parent, int K) {
		
		for (int i = 1; i <= K; i++) {
			for (int j = i; j <= K; j++) {
				
				if (((i-1)+2*(j)-1)<nodes.size()){ // exists node ((i-1)+2*(j)-1)
				
					int St = (i-1);
					
					for (int k = (i-1)+(j); k < nodes.size(); k+=j) {
						if ((k+j-1)<nodes.size()){ //exists node (k+j)
							NormTreeDist(parent,St,k,k-St,createSubNodeList(nodes,St,k-1,parent),createSubNodeList(nodes,k,k+j-1,parent));
							St = k;
						}
					}
				
				}
			}
		}
		
	}

	private double NormTreeDist(Node parent, int first, int second, int combination, Node node1, Node node2) {
		
		EnhancedSimpleTreeMatching estm = new EnhancedSimpleTreeMatching();
		
		double matching = estm.ESTM(node1, node2);
		
		Node match = estm.getCalculatedMatch(node1,node2).first;
		
		double norm = (1-((double)(getTerminals(match))/(double)(Math.max(getTerminals(node1), getTerminals(node2)))));
		
		save(parent,ChildFinder.getChildren(node1).size(),node1.getFirstChild(),ChildFinder.getChildren(node2).size(),node2.getFirstChild(),norm);
		
		return norm;
				
	}

	private void save(Node parent, int combination, Node first, int combination2, Node second,
			double norm) {
		
		Map<Node, Double> map = getInfo(parent,combination,first,combination2);
		
		map.put(second, norm);
		
	}

	private Map<Node, Double> getInfo(Node parent, int combination,
			Node first, int combination2) {
		
		Map<Integer,Map<Node,Double>> map = getInfo(parent,combination,first);
		
		Map<Node,Double> ret = map.get(combination2);
		
		if (ret == null){
			ret = new HashMap<Node, Double>();
			map.put(combination2, ret);
		}
		
		return ret;
	}

	private Map<Integer, Map<Node, Double>> getInfo(Node parent,
			int combination, Node first) {
		
		Map<Node,Map<Integer,Map<Node,Double>>> map = getInfo(parent,combination);
		
		Map<Integer,Map<Node,Double>> ret = map.get(first);
		
		if (ret == null){
			ret = new HashMap<Integer, Map<Node,Double>>();
			map.put(first, ret);
		}
		
		return ret;
	}

	private Map<Node,Map<Integer,Map<Node,Double>>> getInfo(Node parent,int combination) {
		
		Map<Integer,Map<Node,Map<Integer,Map<Node,Double>>>> map = getInfo(parent);
		
		Map<Node,Map<Integer,Map<Node,Double>>> ret = map.get(combination);
		
		if (ret == null){
			ret = new HashMap<Node, Map<Integer,Map<Node,Double>>>();
			map.put(combination, ret);
		}
		
		return ret;
	}

	private Map<Integer,Map<Node,Map<Integer,Map<Node,Double>>>> getInfo(Node parent) {
		
		Map<Node,Map<Integer,Map<Node,Map<Integer,Map<Node,Double>>>>> map = getInfo();
		
		Map<Integer,Map<Node,Map<Integer,Map<Node,Double>>>> ret = map.get(parent);
		
		if (ret == null){
			ret = new HashMap<Integer, Map<Node,Map<Integer,Map<Node,Double>>>>();
			map.put(parent, ret);
		}
		
		return ret;
		
	}

	private Map<Node, Map<Integer, Map<Node, Map<Integer, Map<Node, Double>>>>> getInfo() {
		
		if (info == null){
			info = new HashMap<Node, Map<Integer, Map<Node, Map<Integer, Map<Node, Double>>>>>();
		}
		return info;
		
	}

	private int getTerminals(Node node) {
		
		if (ChildFinder.hasChildren(node)){
			
			int total = 0;
			
			NodeList children = ChildFinder.getChildren(node);
			
			for (int i = 0; i < children.size(); i++) {
				
				total += getTerminals(children.elementAt(i));
				
			}
			
			return total;
			
		}
		
		return 1;
		
	}

	private Node createSubNodeList(NodeList nodes, int start, int end, Node parent) {
		
		Node n = NodeCloner.clone(parent, false, false);
	
		for (int i = start; i <= end; i++) {
			ChildFinder.getChildren(n).add(nodes.elementAt(i));
		}
		
		return n;
	}
	
	public double getDistance(Node node, int combination, Node initialNode1, int combination2, Node initialNode2) {
		
		Map<Node, Double> aux = getInfo(node, combination, initialNode1,combination2);
		
		Double d = aux.get(initialNode2);
		
		return d.doubleValue();
		
	}
	
}
